#pragma once
#include "cinder/gl/TextureFont.h"
#include "cinder/app/AppBasic.h"

#include "Types.h"
#include "Renderer.h"
#include "rendering/SceneNode.h"
#include "ColliderBox.h"
#include "GameObject.h"
#include "Events.h"



using namespace Boon;
using namespace Rendering;

using namespace ci;
using namespace ci::app;



#define TOVEC3f( v ) Vec3f( (v).x, (v).y, (v).z )


class PhysicsNode : public Rendering::SceneNode
{
public:
	PhysicsNode( GameObject& go, const char* name )
		:	SceneNode( go.GetUniqueObjectID(), name, Rendering::PASS_NOT_RENDERED ),
			m_Collider( go.GetComponentBase<Collider>())
	{

	}

	virtual bool IsVisible( Rendering::Scene* scene ) const { return ( m_Collider.Empty() == false ); }

	virtual bool PreRender( Rendering::Scene* scene )
	{
		Transform& t = m_Collider.Lock()->GetGameObject().GetTransform();
		Boon::Quaternion q = t.GetRotation();
		float angle = q.GetAngle();
		angle *= Sign( q.RotateVec( Vector3::RIGHT ).Dot( Vector3::UP ) );
		Vector3 pos = t.GetPosition();
		gl::color( 1, 0, 0 );
		gl::pushMatrices();
		gl::translate( pos.x, pos.y, pos.z );
		gl::rotate( Vec3f( 0, 0, angle ) );
		return true;
	}

	virtual bool PostRender( Boon::Rendering::Scene* scene )
	{
		gl::popMatrices();
		return true;
	}


	ColliderWeak						m_Collider;
};

class PhysicsNodeBox : public PhysicsNode
{
public:
	PhysicsNodeBox( GameObject& go )
		:	PhysicsNode( go, "physics_box_node" )
	{

	}

	virtual bool Render( Boon::Rendering::Scene* scene )
	{
		Bounds bounds = static_cast<ColliderBox*>( m_Collider.Lock().Get() )->GetBounds();

		//front
		gl::drawLine( TOVEC3f( bounds.GetFrontLeftBottomLocal() ),
			TOVEC3f( bounds.GetFrontLeftTopLocal() ) );

		gl::drawLine( TOVEC3f( bounds.GetFrontLeftTopLocal() ),
			TOVEC3f( bounds.GetFrontRightTopLocal() ) );

		gl::drawLine( TOVEC3f( bounds.GetFrontRightTopLocal() ),
			TOVEC3f( bounds.GetFrontRightBottomLocal() ) );

		gl::drawLine( TOVEC3f( bounds.GetFrontRightBottomLocal() ),
			TOVEC3f( bounds.GetFrontLeftBottomLocal() ) );

		//back
		gl::drawLine( TOVEC3f( bounds.GetBackLeftBottomLocal() ),
			TOVEC3f( bounds.GetBackLeftTopLocal() ) );

		gl::drawLine( TOVEC3f( bounds.GetBackLeftTopLocal() ),
			TOVEC3f( bounds.GetBackRightTopLocal() ) );

		gl::drawLine( TOVEC3f( bounds.GetBackRightTopLocal() ),
			TOVEC3f( bounds.GetBackRightBottomLocal() ) );

		gl::drawLine( TOVEC3f( bounds.GetBackRightBottomLocal() ),
			TOVEC3f( bounds.GetBackLeftBottomLocal() ) );

		// side left
		gl::drawLine( TOVEC3f( bounds.GetBackLeftBottomLocal() ),
			TOVEC3f( bounds.GetFrontLeftBottomLocal() ) );

		gl::drawLine( TOVEC3f( bounds.GetBackLeftTopLocal() ),
			TOVEC3f( bounds.GetFrontLeftTopLocal() ) );

		// side right
		gl::drawLine( TOVEC3f( bounds.GetBackRightBottomLocal() ),
			TOVEC3f( bounds.GetFrontRightBottomLocal() ) );

		gl::drawLine( TOVEC3f( bounds.GetBackRightTopLocal() ),
			TOVEC3f( bounds.GetFrontRightTopLocal() ) );
		gl::color( 1, 1, 1 );
		gl::drawSphere( Vec3f( 0, 0, 0 ), .2f );
		return true;
	}

};

class PhysicsNodeSphere : public PhysicsNode
{
public:
	PhysicsNodeSphere( GameObject& go )
		:	PhysicsNode( go, "physics_sphere_node" )
	{

	}

	virtual bool Render( Boon::Rendering::Scene* scene )
	{
		gl::drawStrokedCircle( Vec2f( 0, 0 ), static_cast<ColliderSphere*>( m_Collider.Lock().Get() )->GetRadius() );
		gl::color( 1, 1, 1 );
		gl::drawSphere( Vec3f( 0, 0, 0 ), .2f );
		return true;
	}

};

class PhysicsRenderer : public Boon::Renderer
{
	GAMECOMPONENT( PhysicsRenderer, Renderer )

	virtual IMessengerEventStrong GetEventCreateSceneNode() 
	{
		ColliderWeak c = GetGameObject().GetComponentBase<Collider>();
		if( c.Empty() )
			throw BoonException( "PhysicsRenderer", "GetEventCreateSceneNode", "Gameobject has no collider" );

		switch( c.Lock()->GetType() )
		{
		case CT_BOX:
			m_Node = Shared_ptr<PhysicsNodeBox>( new PhysicsNodeBox( GetGameObject() ) );
			break;
		case CT_SPHERE:
			m_Node = Shared_ptr<PhysicsNodeSphere>( new PhysicsNodeSphere( GetGameObject() ) );
			break;
		case CT_CAPSULE:
			//m_Node = Shared_ptr<PhysicsNodeBox>( new PhysicsNodeBox( GetGameObject() ) );
			break;
		}
		m_RenderPass = PASS_NOT_RENDERED;
		return IMessengerEventStrong( new Event_RenderComp_Created( *this, m_Node ) );
	}

	Shared_ptr<PhysicsNode>			m_Node;


};

